Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
Changes since DR3
1) Updated definition of kODPropPreAnnotation.
Changes since DR2
1) Corrected usage of ODStorageUnit::GetStrongStorageUnitRef.
2) Refcounting methods name change.
3) Added section on Persistent Object Annotations.
Overview
Every OpenDoc Draft contains a network of Storage Units. These Storage Units are "connected" to each other through persistent references (for more information, please refer to "Persistent Reference" document). When an OpenDoc Draft is opened, ODPersistentObjects and ODStorageUnit objects are instantiated using the Storage Units.
Copying an object is not as straightforward as the usual stream operations. For example, when one tries to copy a Value, one needs to copy all the Storage Units that are being referred to by the Value also. Otherwise, the data copied will not be complete.
In order to help developers accomplish this task more easily, OpenDoc provides a cloning mechanism to enable deep-copy through a fairly simple API. This AP I has the following characteristics:
1) Clients have to go through source ODDraft to clone any object. ODDraft will in turn call the appropriate object's CloneInto method. Clients should NEVER call the CloneInto method of another object directly.
2) Cloning is transacted operation. Therefore, clients have to start the Cloning operation by calling BeginClone and commit the Cloning operation by calling EndClone. If for any reason, cloning cannot be completed, the client should call AbortClone to abort the transaction.
3) The cloning mechanism is biased toward running objects. Therefore, if there is a running object corresponding to a particular Storage Unit, the cloning mechanism will call the running object's CloneInto method instead of the Storage Unit's. This ensures that Externalize does not need to be called before any data interchange.
4) The Cloning mechanism respects a scope and the scope can only be a Frame object or a Storage Unit belonging to a Frame.
5) There are different kinds of cloning. They are mainly for data interchange purposes. Please refer to the Clipboard recipe document for more information.
6) The ids returned from Clone and WeakClone cannot be used between BeginClone and EndClone. Therefore, if you want to use one of those ids for getting the object or making a reference, you will have to wait till after EndClone.
7) Some of ids returned may turn out to be invalid because the corresponding objects have not been strongly cloned. Please refer to the appropriate recipes to determine what to do in specific situations.
Recipe for Clients
Clients need to use the following ODDraft API calls:
ODDraftKey BeginClone(in ODDraft destDraft, in ODFrame destFrame, in ODCloneKind kind);
void EndClone(in ODDraftKey key);
void AbortClone(in ODDraftKey key);
ODID Clone(in ODDraftKey key,
in ODID fromObjectID,
in ODID toObjectID,
in ODID scope);
ODID WeakClone(in ODDraftKey key,
in ODID objectID,
in ODID toObjectID,
in ODID scope);
BeginClone, EndClone and AbortClone are for setting up and terminating the cloning transaction. Clone and WeakClone are used to notify the Draft that the specified object needs to be cloned.
1) Given the following network of objects,
If the client wants to clone A, s/he needs to do the following:
The result is that C and D are copied. B is not copied because it is not strongly referenced by C or any objects transitively strongly referenced by C. The newB returned by WeakClone is an invalid ID. Therefore, if the client tries to get that object using newB, an error will be resulted. It is the responsibilty of the client to handle this error condition.
Also, the IDs can only be used to make Storage Unit References during Cloning. The client should not try to get the persistent object or storage unit.
The following diagram shows what the copied network of Storage Units/Persistent Objects looks like:
3) If the network in 2) looks like the following, the cloneresult will be different.
In this case, the code fragment in 2) will copy C, D and B. The returned newB will then be a valid ID referring to the new copy of B. The following diagram shows what the resulting network looks like:
Recipe for Parts:
All Persistent Objects (including Parts) have to override CloneInto in ODPersistentObject:
void CloneInto(in ODDraftKey key,
in ODStorageUnit toSU,
in ODFrame scope);
During the Clone operation (i.e, between BeginClone and EndClone), a Persistent Object's CloneInto method is called by the draft to do the actual data copying. The recipe is very similar to that for the clients except that the Persistent Object has to write out its intrinsic content in addition to cloning its referenced Storage Units/Persistent Objects.
The following is an example of what a Part Editor may do in response to CloneInto:
void MyPartCloneInto(MyPart* somSelf,
Environment* ev,
ODDraftKey key,
ODStorageUnit toSU,
OID scopeFrameID)
{
// Since this method may be called multiple times during one Cloning
// transaction, the Part should check to see whether it really needs
// to write out any data.
if (toSU->Exists(ev, kODPropContents, kMyContentKind, 0) == kODFalse) {
If a Part has two frames and the user only chooses to copy one frame, the Part should be notified about it. Otherwise, the Part may copy too much data or in certain cases the wrong data.
Let's consider the following situation. Part A contains Part B (which has two display frames). In turn, Part B contains Part C and Part D.
If the user selects some intrinsic content with B1 (as shown in the thin box) and selects Copy, Part A should do the following:
As a result, Part B's CloneInto will be called twice (once with Frame B1 as the scope and another time with Frame B2 as the scope). Part B should be ready to handle this and write out the Part Content accordingly.
For more information on Data Interchange, please refer to the Data Interchange Recipes (especially Clipboard Recipes).
Destination Frame for Cloning
When the ODCloneKind argument to BeginClone is kODClonePaste or kODCloneDropMove, parts must supply as the ODFrame argument the frame that is performing the paste or receiving the drop. For other values of ODCloneKind, the frame may be kODNULL, as in the examples in this document. This parameter allows OpenDoc to determine if the destination is a valid location for content being moved; an invalid destination frame is one which would cause a display frame of a part to be embedded within another display frame of the same part. In this unusual situation, the subsequent call to EndClone will raise an exception. If EndClone returns an error, parts must call AbortClone (which does not return errors).
Persistent Object Annotations
As discussed above, the cloning mechanism is biased toward runtime objects. Therefore, if there is anything in the storage unit that is not stored by the runtime persistent object itself, it is not going to be cloned. An example of this may be a utility like a spell checker. It may store a list of words in the part's storage unit.
In order to make sure that this information is not lost, the Container Suite copies any properties with the following prefix to the destination storage unit: